package link.luyu.demo.port.config;

import com.moandjiezana.toml.Toml;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

/*
[connection]
    server =  '127.0.0.1:8250'
    sslKey = 'classpath:ssl.key'
    sslCert = 'classpath:ssl.crt'
    caCert = 'classpath:ca.crt'
    sslSwitch = 2  # disable ssl:2, SSL without client auth:1 , SSL with client and server auth: 0

[db]
    # for connect database
    url = 'jdbc:h2:file:./db/port_service' # local storage in file
    username = 'root'
    password = ''
*/

public class ApplicationConfig {
    private static Logger logger = LoggerFactory.getLogger(ApplicationConfig.class);

    private Chain chain;
    private DB db;

    public static Toml readToml(String fileName) {
        try {
            Path path;
            if (fileName.indexOf("classpath:") != 0) {
                path = Paths.get(fileName);
            } else {
                PathMatchingResourcePatternResolver resolver =
                        new PathMatchingResourcePatternResolver();
                path = Paths.get(resolver.getResource(fileName).getURI());
            }

            byte[] bytes = Files.readAllBytes(path);
            return new Toml().read(new String(bytes));
        } catch (java.io.IOException ioe) {
            return new Toml();
        }
    }

    public static ApplicationConfig parseFromFile(String filePath) {
        logger.info("ApplicationConfig parseFromFile ...");
        Toml toml = readToml(filePath);
        ApplicationConfig config = new ApplicationConfig(toml);
        return config;
    }

    public ApplicationConfig(Toml toml) {
        this.db = new DB(toml);
        this.chain = new Chain(toml);
    }

    public Chain getChain() {
        return chain;
    }

    public void setChain(Chain chain) {
        this.chain = chain;
    }

    public DB getDb() {
        return db;
    }

    public void setDb(DB db) {
        this.db = db;
    }

    @Override
    public String toString() {
        return "ApplicationConfig {\n" + "chain=" + chain + "\n" + "db=" + db + "\n}";
    }

    public static class Resource {
        public String chainPath;
        public String portContract;

        public Resource(String cp, String pc) {
            this.chainPath = cp;
            this.portContract = pc;
        }

        @Override
        public String toString() {
            return "Peer(" + chainPath + "." + portContract + ")";
        }

        public String asPath() {
            return chainPath + "." + portContract;
        }
    }

    public static class Chain {
        private String path;
        private String portContract;
        private String portContractABI;
        private String supplyContract;
        private String supplyContractABI;
        private String luyuIdentity;
        private String luyuSecKey;
        private String luyuPubKey;
        private List<Resource> peers;

        Chain(Toml toml) {
            this.path = parseString(toml, "chain.path", "payment.brochain");
            this.portContract = parseString(toml, "chain.portContract", "portA");
            this.portContractABI = parseString(toml, "chain.portContractABI", "");
            this.supplyContract = parseString(toml, "chain.supplyContract", "supplyA");
            this.supplyContractABI = parseString(toml, "chain.supplyContractABI", "");
            this.luyuIdentity = parseString(toml, "chain.luyuIdentity", "luyu-identity");
            this.luyuSecKey = parseString(toml, "chain.luyuSecKey", "0");
            if (this.luyuSecKey.indexOf("0x") == 0) {
                this.luyuSecKey = this.luyuSecKey.substring(2);
            }
            this.luyuPubKey = parseString(toml, "chain.luyuPubKey", "0");
            if (this.luyuPubKey.indexOf("0x") == 0) {
                this.luyuPubKey = this.luyuPubKey.substring(2);
            }
            this.peers = new LinkedList();
            List<String> peerList = toml.getList("chain.peers");
            if (peerList != null) {
                for (String s : peerList) {
                    try {
                        String[] r = s.split("\\.");
                        this.peers.add(new Resource(r[0] + "." + r[1], r[2]));
                    } catch (Exception e) {
                        logger.error("error parse: " + s);
                    }
                }
            }

            logger.info("Load configuration: " + this.toString());
        }

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public String getPortContract() {
            return portContract;
        }

        public void setPortContract(String pc) {
            this.portContract = pc;
        }

        public String getPortContractABI() {
            return portContractABI;
        }

        public void setPortContractABI(String pc) {
            this.portContractABI = pc;
        }

        public String getSupplyContract() {
            return supplyContract;
        }

        public void setSupplyContract(String sc) {
            this.supplyContract = sc;
        }

        public String getSupplyContractABI() {
            return supplyContractABI;
        }

        public void setSupplyContractABI(String pc) {
            this.supplyContractABI = pc;
        }

        public String getLuyuIdentity() {
            return luyuIdentity;
        }

        public void setLuyuIdentity(String li) {
            this.luyuIdentity = li;
        }

        public String getLuyuSecKey() {
            return luyuSecKey;
        }

        public byte[] getLuyuSecKeyAsBytes() {
            BigInteger bi = new BigInteger(luyuSecKey, 16);
            return bi.toByteArray();
        }

        public void setLuyuSecKey(String k) {
            this.luyuSecKey = k;
        }

        public String getLuyuPubKey() {
            return luyuPubKey;
        }

        public byte[] getLuyuPubKeyAsBytes() {
            BigInteger bi = new BigInteger(luyuPubKey, 16);
            return bi.toByteArray();
        }

        public void setLuyuPubKey(String k) {
            this.luyuPubKey = k;
        }

        public List<Resource> getPeerResources() {
            return this.peers;
        }

        public void setPeerResources(List<Resource> ps) {
            this.peers = ps;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(
                    "Chain {"
                            + "path='"
                            + path
                            + '\''
                            + ", portContract='"
                            + portContract
                            + '\''
                            + ", portContractABI='"
                            + portContractABI
                            + '\''
                            + ", supplyContract='"
                            + supplyContract
                            + '\''
                            + ", supplyContractABI='"
                            + supplyContractABI
                            + '\''
                            + ", luyuIdentity='"
                            + luyuIdentity
                            + '\''
                            + ", luyuSecKey='"
                            + luyuSecKey // security ?
                            + '\''
                            + ", luyuPubKey='"
                            + luyuPubKey
                            + '\'');

            sb.append(" peers=[");
            for (Resource r : peers) {
                sb.append(r.toString() + ",");
            }
            sb.append("]}");
            return sb.toString();
        }
    }

    public static class DB {
        private String url;
        private String username;
        private String password;

        DB(Toml toml) {
            this.url = parseString(toml, "db.url", "jdbc:h2:file:./db/port_service");
            this.username = parseString(toml, "db.username", "root");
            this.password = parseString(toml, "db.password", "");
            logger.info("Load configuration: " + this.toString());
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        @Override
        public String toString() {
            return "DB {"
                    + "url='"
                    + url
                    + '\''
                    + ", username='"
                    + username
                    + '\''
                    + ", password='"
                    + password
                    + '\''
                    + '}';
        }
    }

    private static long parseLong(Toml toml, String key, long defaultValue) {
        Long i = toml.getLong(key);
        return i == null ? defaultValue : i;
    }

    private static String parseString(Toml toml, String key, String defaultValue) {
        String s = toml.getString(key);
        return s == null ? defaultValue : s;
    }
}
